#include "esp_common.h"
#include "freertos/task.h"
#include "gpio.h"
#include "esp_wifi.h"
#include "esp_sta.h"
#include "esp_softap.h"
#include "esp_libc.h"
#include "lwip/sockets.h"
#include "lwip/def.h"
#include "freertos/portmacro.h"
#include "lwip/multi-threads/sockets_mt.h"

#define BLINK_GPIO 2
#define ON 1
#define OFF 0

#define ESP8266_STATION_SSID "nicxspirit2"
#define ESP8266_STATION_PASSWORD ""

#define ESP8266_AP_SSID "esp8266"
#define ESP8266_AP_PASSWORD "12345678"

#define SERVER_PORT = 3456

/******************************************************************************
 * FunctionName : user_rf_cal_sector_set
 * Description  : SDK just reversed 4 sectors, used for rf init data and paramters.
 *                We add this function to force users to set rf cal sector, since
 *                we don't know which sector is free in user's application.
 *                sector map for last several sectors : ABCCC
 *                A : rf cal
 *                B : rf init data
 *                C : sdk parameters
 * Parameters   : none
 * Returns      : rf cal sector
 *******************************************************************************/
uint32 user_rf_cal_sector_set(void)
{
    flash_size_map size_map = system_get_flash_size_map();
    uint32 rf_cal_sec = 0;
    switch (size_map)
    {
    case FLASH_SIZE_4M_MAP_256_256:
        rf_cal_sec = 128 - 5;
        break;

    case FLASH_SIZE_8M_MAP_512_512:
        rf_cal_sec = 256 - 5;
        break;

    case FLASH_SIZE_16M_MAP_512_512:
    case FLASH_SIZE_16M_MAP_1024_1024:
        rf_cal_sec = 512 - 5;
        break;

    case FLASH_SIZE_32M_MAP_512_512:
    case FLASH_SIZE_32M_MAP_1024_1024:
        rf_cal_sec = 1024 - 5;
        break;

    default:
        rf_cal_sec = 0;
        break;
    }

    return rf_cal_sec;
}

void task_blink(void *ignore)
{
    int count = 0;
    while (true)
    {
        system_soft_wdt_feed(); // 软件喂狗 防止复位
        GPIO_OUTPUT_SET(GPIO_ID_PIN(BLINK_GPIO), OFF);
        vTaskDelay(1000 / portTICK_RATE_MS);
        GPIO_OUTPUT_SET(GPIO_ID_PIN(BLINK_GPIO), ON);
        vTaskDelay(1000 / portTICK_RATE_MS);
        printf("[LOG] count: %d\n", count++);
    }

    vTaskDelete(NULL);
}

void smartconfig_task(void *ignore)
{
    wifi_station_disconnect(); //
    smartconfig_set_type(SC_TYPE_ESPTOUCH_AIRKISS);
    // smartconfig_start();
}

void tcp_server_start(void *ignore)
{
    // TCP Server
    int32 listenfd;
    int32 ret;
    struct sockaddr_in server_addr, remote_addr;
    int stack_counter = 0;

    /* Construct local address structure */
    memset(&server_addr, 0, sizeof(server_addr)); /* Zero out structure */
    server_addr.sin_family = AF_INET;             /* Internet address family */
    server_addr.sin_addr.s_addr = INADDR_ANY;     /* Any incoming interface */
    server_addr.sin_len = sizeof(server_addr);
    // server_addr.sin_port = htons(httpd_server_port); /* Local port */
    server_addr.sin_port = htons(8866); /* Local port */

    /* Create socket for incoming connections */
    do
    {
        listenfd = socket(AF_INET, SOCK_STREAM, 0);
        if (listenfd == -1)
        {
            printf("ESP8266 TCP server task > socket error\n");
            vTaskDelay(1000 / portTICK_RATE_MS);
        }
    } while (listenfd == -1);

    printf("ESP8266 TCP server task > create socket: %d\n", listenfd);

    /* Bind to the local port */
    do
    {
        ret = bind(listenfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
        if (ret != 0)
        {
            printf("ESP8266 TCP server task > bind fail\n");
            vTaskDelay(1000 / portTICK_RATE_MS);
        }
    } while (ret != 0);

    printf("ESP8266 TCP server task > port: %d\n", ntohs(server_addr.sin_port));

    do
    {
        /* Listen to the local connection */
        // ret = listen(listenfd, MAX_CONN);
        ret = listen(listenfd, 100);
        if (ret != 0)
        {
            printf("ESP8266 TCP server task > failed to set listen queue!\n");
            vTaskDelay(1000 / portTICK_RATE_MS);
        }
    } while (ret != 0);
    printf("ESP8266 TCP server task > listen ok\n");

    int32 client_sock;
    int32 len = sizeof(struct sockaddr_in);
    int recbytes;

    for (;;)
    {
        printf("ESP8266 TCP server task > wait client\n");
        /*block here waiting remote connect request*/
        if ((client_sock = accept(listenfd, (struct sockaddr *)&remote_addr, (socklen_t *)&len)) < 0)
        {
            printf("ESP8266 TCP server task > accept fail\n");
            continue;
        }
        printf("ESP8266 TCP server task > Client from %s %d\n",
               inet_ntoa(remote_addr.sin_addr), htons(remote_addr.sin_port));
        char *recv_buf = (char *)zalloc(128);
        while ((recbytes = read(client_sock, recv_buf, 128)) > 0)
        {
            recv_buf[recbytes] = 0;
            printf("ESP8266 TCP server task > read data success %d!\nESP8266 TCP server task > %s\n", recbytes, recv_buf);
        }
        free(recv_buf);
        if (recbytes <= 0)
        {
            printf("ESP8266 TCP server task > read data fail!\n");
            close(client_sock);
        }
    }
}

void scan_done(void *arg, STATUS status)
{
    uint8 ssid[33];
    char temp[128];
    if (status == OK)
    {
        struct bss_info *bss_link = (struct bss_info *)arg;
        while (bss_link != NULL)
        {
            memset(ssid, 0, 33);
            if (strlen(bss_link->ssid) <= 32)
                memcpy(ssid, bss_link->ssid, strlen(bss_link->ssid));
            else
                memcpy(ssid, bss_link->ssid, 32);
            printf("[LOG] (%d,\"%s\",%d,\"" MACSTR "\",%d)\r\n",
                   bss_link->authmode, ssid, bss_link->rssi,
                   MAC2STR(bss_link->bssid), bss_link->channel);
            bss_link = bss_link->next.stqe_next;
        }
    }
    else
    {
        printf("[LOG] scan fail !!!\r\n");
    }
}

/******************************************************************************
 * FunctionName : user_init
 * Description  : entry of user application, init user function here
 * Parameters   : none
 * Returns      : none
 *******************************************************************************/
void user_init(void)
{
    xTaskCreate(&task_blink, "startup", 2048, NULL, 1, NULL);

    // 初始化操作
    printf("[LOG] SDK version: %s\n", system_get_sdk_version());
    printf("[LOG] ESP8266 chip ID:0x%x\n", system_get_chip_id());

    // set wifi
    // wifi_set_opmode(STATION_MODE);
    // wifi_station_scan(NULL, scan_done);

    wifi_set_opmode(STATION_MODE);
    // wifi_set_opmode(STATIONAP_MODE);

    // station mode config
    struct station_config *config = (struct station_config *)zalloc(sizeof(struct station_config));
    sprintf(config->ssid, ESP8266_STATION_SSID);
    sprintf(config->password, ESP8266_STATION_PASSWORD);
    wifi_station_set_config(config);
    free(config);
    bool conn = wifi_station_connect();
    printf("[LOG] wifi station connect: %d\n", conn);

    // soft-AP mode config
    // initialization
    /*struct softap_config *configsoft = (struct softap_config *)zalloc(sizeof(struct softap_config));
    wifi_softap_get_config(configsoft); // Get soft-AP config first.

    sprintf(configsoft->ssid, ESP8266_AP_SSID);
    sprintf(configsoft->password, ESP8266_AP_PASSWORD);
    configsoft->authmode = AUTH_WPA_WPA2_PSK;
    configsoft->ssid_len = 0; // or its actual SSID length
    configsoft->max_connection = 4;
    wifi_softap_set_config(configsoft); // Set ESP8266 soft-AP config
    free(configsoft);*/

    printf("[LOG] smartconfig version: %s\n", smartconfig_get_version());
    // set smartconfig task
    // xTaskCreate(&smartconfig_task, "smartconfig_task", 256, NULL, 2, NULL);

    // tcp server
    // xTaskCreate(&tcp_server_start, "tcp_server_task", 1024, NULL, 2, NULL);

    // 配置 GPIO2 为普通 IO 口
    PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2);
}
